

(function() {
    "use strict";
    const Cu = Components.utils;
    const Ci = Components.interfaces;
    const Cc = Components.classes;
    const Cr = Components.results;
    function require(url) {
        let scope = {};
        Cu.import(url, scope);
        return scope;
    }
    let {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
    let {StrUtils} = require("chrome://jsm/content/utils.js");
    let {Services} = require("resource://gre/modules/Services.jsm");
    let {console} = require("resource://gre/modules/devtools/Console.jsm");

    const im_strre = "(?:[^\"\\\\]|\\\\[0btnvfr\"\'\\\\])+";
    const runPattern = new RegExp('^imacros://run/\\?(code|m)=(.*)');
    const jsPattern = new RegExp('^javascript:\\(function\\(\\) '+
                                 '\\{(?:try\\{)?var ((?:e_)?m(?:64)?) = '+
                                 '"('+im_strre+')"'+
                                 ', n(?:64)? = '+
                                 '"('+im_strre+')";');
    return {
        classDescription: "iMacros Request Watcher",
        classID: Components.ID("{e4a28173-828d-486e-937c-4c8e4d7a8511}"),
        contractID: "@iopus.com/requestwatcher;1",
        xpcom_categories: [{category: "content-policy"}],

        init() {
            let registrar = Components.manager
                .QueryInterface(Ci.nsIComponentRegistrar);
            registrar.registerFactory(
                this.classID, this.classDescription, this.contractID, this
            );
            let catMan = Cc["@mozilla.org/categorymanager;1"]
                .getService(Ci.nsICategoryManager);
            for(let cat of this.xpcom_categories)
                catMan.addCategoryEntry(
                    cat.category, (cat.entry || this.contractID),
                    this.contractID,
                    false, true
                );
            ["filter-on", "filter-off", 
             "update-whitelist", "terminate",
             "run-macro-completed"].forEach(
                 topic => addMessageListener("imacros:"+topic, this)
             );
        },
        
        terminate() {
            ["filter-on", "filter-off", 
             "update-whitelist", "terminate", 
             "run-macro-completed"].forEach(
                 topic => removeMessageListener("imacros:"+topic, this)
             );

            let catMan = Cc["@mozilla.org/categorymanager;1"]
                .getService(Ci.nsICategoryManager);
            for (let category of this.xpcom_categories)
                catMan.deleteCategoryEntry(
                    category, this.contractID, false
                );
            let registrar = Components.manager
                .QueryInterface(Ci.nsIComponentRegistrar);
            
            Cu.reportError("unregisterFactory()");
            return Promise.resolve().then( 
                () => registrar.unregisterFactory(this.classID, this)
            );
        },

        receiveMessage(msg) {
            let topic = msg.name.replace(/^imacros:/, "");
            switch(topic) {
            case "filter-on": 
                this.enableImageFilter();
                return;
            case "filter-off":
                this.disableImageFilter();
                return;
            case "update-whitelist":
                this.loadList(msg.data);
                return;
            case "run-macro-completed":
                this.lastMatchedURI = "";
                return;
            case "terminate":
                this.terminate();
                return;
            }
        },

        createInstance(outer, iid) {
            if (outer)
                throw Cr.NS_ERROR_NO_AGGREGATION;
            return this.QueryInterface(iid);
        },

        QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, 
                                               Ci.nsIFactory]),

        whiteList: new Map(),
        filterImages: false,

        
        loadList(list) {
            this.whiteList.clear();
            for (let site in list)
                this.whiteList.set(site, list[site]);
        },

        
        enableImageFilter() {
            this.filterImages = true;
        },

        disableImageFilter() {
            this.filterImages = false;
        },
        
        lastMatchedURI: "",

        
        shouldBlockURI(location, origin, principal) {
            let is_js = location.scheme == "javascript";
            let is_im = location.scheme == "imacros";
            let url = decodeURIComponent(location.spec);
            let match = (is_js && jsPattern.exec(url)) ||
                (is_im && runPattern.exec(url));
            if (!match)
                return false;
            let retobj = new Object();
            
            if (is_js) {
                retobj.type = "bookmarklet";
                retobj.data = match[1] == "m" ?
                    StrUtils.unwrap('"'+match[2]+'"') :
                    decodeURIComponent(atob(match[2]));
                retobj.name = /64$/.test(match[1]) ?
                    decodeURIComponent(atob(match[3])) : match[3];
            } else {
                retobj.type = match[1];
                retobj.data = match[2];
                if (match[1] == "m" && /\/?([^\/]*)$/.test(match[2]))
                    retobj.name = RegExp.$1;
            }
            retobj.grant = false;
            retobj.origin = "unknown";
            if (origin) {
                if (origin.schemeIs("file") ||
                    origin.schemeIs("chrome")) {
                    retobj.grant = true;
                    retobj.origin = "local";
                } else {
                    retobj.grant = false;
                    retobj.origin = origin.host;
                    if (this.whiteList.has(origin.host)) {
                        if (!this.whiteList.get(origin.host))
                            return true; 
                        retobj.grant = true;
                    } 
                }
            } else {
                if (!principal) {
                    
                    
                    retobj.grant = true;
                    retobj.origin = "local";
                } else {
                    retobj.grant = false;
                    retobj.origin = origin.host;
                    if (this.whiteList.has(origin.host)) {
                        if (!this.whiteList.get(origin.host))
                            return true; 
                        retobj.grant = true;
                    }
                }
            }
            
            
            if (this.lastMatchedURI != location.spec) {
                sendAsyncMessage("imacros:run-macro", retobj);
                this.lastMatchedURI = location.spec;
            }
            
            return true;
        },
        
        
        shouldLoad(type, location, origin, ctx, mime, extra, principal) {
            if (type == Ci.nsIContentPolicy.TYPE_DOCUMENT) {
                return this.shouldBlockURI(location, origin, principal) ?
                    Ci.nsIContentPolicy.REJECT_REQUEST : 
                    Ci.nsIContentPolicy.ACCEPT;
            } else if (type == Ci.nsIContentPolicy.TYPE_OBJECT) {
                if (this.filterImages) {
                    
                    if (/^application\/x-shockwave-flash$/.test(mime) ||
                        /\.swf$/i.test(location.spec))
                        return Ci.nsIContentPolicy.REJECT_REQUEST;
                }
            } else if (type == Ci.nsIContentPolicy.TYPE_IMAGE) {
                if (this.filterImages) {
                    
                    if (/^chrome:\/\//.test(location.spec) ||
                        /favicon\.ico$/.test(location.spec) )
                        return Ci.nsIContentPolicy.ACCEPT;
                    return Ci.nsIContentPolicy.REJECT_REQUEST;
                }
            } 

            return Ci.nsIContentPolicy.ACCEPT;
        },
        
        shouldProcess(type, location, origin, ctx, mime, extra) {
            return Ci.nsIContentPolicy.ACCEPT;
        },
    };

})().init();











